package cn.siat.file.service;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.siat.common.core.constant.Constants;
import cn.siat.common.core.domain.R;
import cn.siat.common.core.exception.ServiceException;
import cn.siat.common.core.utils.FileUploadUtils;
import cn.siat.common.core.utils.StringUtils;
import cn.siat.common.core.utils.file.FileUtils;
import cn.siat.common.core.web.domain.AjaxResult;
import org.springframework.http.HttpStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;

/**
 * 本地文件存储
 * 
 * @author siat
 */
@Primary
@Service
public class LocalSysFileServiceImpl implements ISysFileService
{
    /**
     * 资源映射路径 前缀
     */
    @Value("${file.prefix}")
    public String localFilePrefix;

    /**
     * 域名或本机访问地址
     */
    @Value("${file.domain}")
    public String domain;
    
    /**
     * 上传文件存储在本地的根路径
     */
    @Value("${file.path}")
    private String localFilePath;
    @Value("${path.sequence}")
    private String sequencePath;
    // 转换文件最长250M

    @Override
    public ResponseEntity<?> uploadBigFile(MultipartFile file, String identifier, String chunkNumber, Long totalSize)  {
        String path= localFilePath + File.separator + identifier;
        String resultPath = FileUploadUtils.uploadMultipartFile(file, path, chunkNumber);
        Charset charset = CharsetUtil.charset(FileUtils.getCharset(new File(resultPath)));
        if(totalSize> Constants.MAX_COVER_FILE_SIZE && !CharsetUtil.CHARSET_UTF_8.equals(charset) && "1".equals(chunkNumber)){
            return new ResponseEntity<>("请将文件转换为UTF-8后上传",HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return ResponseEntity.ok(R.ok("上传成功", resultPath));
    }
    @Override
    public R<?> checkIsUtf8(MultipartFile file,Long fileSize) throws IOException {
        File temFolder = new File(localFilePath+"/temp");
        temFolder.mkdirs();
        File f = new File(temFolder.getPath()+"/"+IdUtil.simpleUUID());
        file.transferTo(f);
        Charset charset = CharsetUtil.charset(FileUtils.getCharset(f));
        if(!CharsetUtil.CHARSET_UTF_8.equals(charset) && fileSize>Constants.MAX_COVER_FILE_SIZE){
            return R.fail("请将文件转换为UTF-8后上传");
        }
        f.delete();
        return R.ok();
    }
    @Override
    public String mergeBigFile(String identifier,String filename,String totalSize,String folderPath,Integer isConvertUtf8) throws IOException {
        String identifierPath = localFilePath + File.separator + identifier;
        String path;
        if(StringUtils.isNotEmpty(folderPath)){
            path = sequencePath+File.separator +folderPath+File.separator+ totalSize+"-"+filename;
            File targetFolder = new File(sequencePath+File.separator +folderPath);
            if(!targetFolder.exists()){
                targetFolder.mkdirs();
            }
        }else{
            path = localFilePath+File.separator + totalSize+"-"+filename;
        }
        File newFile = new File(path);
        if(newFile.exists()){
            newFile.delete();
        }
        File file = new File(identifierPath);
        File[] files = file.listFiles();
        // 排序,按序读取
        List<File> fileList = Arrays.asList(files);
        fileList.sort((file1,file2)->{
            int a = Integer.valueOf(file1.getName());
            int b = Integer.valueOf(file2.getName());
            return a- b;
        });
        FileInputStream inputStream;
        byte[] byt = new byte[5 * 1024 * 1024];
        int len;
        FileOutputStream outputStream = new FileOutputStream(newFile,  true);
        for (File f: fileList){
            inputStream = new FileInputStream(f);
            while ((len = inputStream.read(byt))!=-1) {
                outputStream.write(byt, 0, len);
            }
            inputStream.close();
        }
        for (File f: files){
            f.delete();
        }
        file.delete();
        outputStream.close();
        if(newFile.getName().endsWith(".gz")){
            String decompressPath=newFile.getParentFile().getPath();
            String decompressName = FileUploadUtils.decompress(newFile,decompressPath);
            newFile = new File(decompressPath+File.separator+decompressName);
        }
        Charset newFileCharset = CharsetUtil.charset(FileUtils.getCharset(newFile));
        if(Integer.valueOf(1).equals(isConvertUtf8) && !CharsetUtil.CHARSET_UTF_8.equals(newFileCharset)){
            if(newFile.length()>Constants.MAX_COVER_FILE_SIZE){
                throw new ServiceException("文件过大,无法转换编码");
            }else{
                CharsetUtil.convert(newFile,newFileCharset,CharsetUtil.CHARSET_UTF_8);
            }
        }
        return newFile.getName();
    }
    private List<String> splitFile(File splitFile,File splitFolder,long splitSmallFileSize) throws IOException{
        //被分割文件总大小
        long splitFileSize=splitFile.length();
        //每个小文件分割的起始位置
        long splitSmallFileBeginPos;
        //实际分割的小文件大小
        long splitSmallFileActualSize;
        //被分割的小文件个数
        int size=(int)Math.ceil(splitFileSize*1.0/splitSmallFileSize);
        //被分割的小文件路径list
        List<String> splitSmallFileList=new ArrayList<>();
        for (int i = 0; i < size; i++) {
            splitSmallFileList.add(splitFolder.getPath()+"/"+i+"_"+splitFile.getName());
        }
        //切割文件
        for (int i = 0; i < size; i++) {
            //切割起始位置
            splitSmallFileBeginPos=i*splitSmallFileSize;
            //切割到最后一个小文件
            if (i==size-1) {
                //切割的实际大小
                splitSmallFileActualSize=splitFileSize;
            }else {//否则
                //切割的实际大小
                splitSmallFileActualSize=splitSmallFileSize;
                //源文件减小
                splitFileSize-=splitSmallFileActualSize;
            }
            //具体的切割
            //源文件
            RandomAccessFile splitRandomAccessFile=new RandomAccessFile(splitFile, "r");
            //被分割的小文件
            RandomAccessFile splitSmallRandomAccessFile=new RandomAccessFile(splitSmallFileList.get(i), "rw");
            //从源文件的哪个位置读取
            splitRandomAccessFile.seek(splitSmallFileBeginPos);
            //分段读取
            //10字节的缓存
            byte[] cache=new byte[1024*10];
            int len=-1;
            while((len=splitRandomAccessFile.read(cache))!=-1) {
                //小文件实际分割大小>len
                if (splitSmallFileActualSize>len) {
                    splitSmallRandomAccessFile.write(cache,0,len);
                    splitSmallFileActualSize-=len;
                }else {//小文件实际分割大小<len，写完数据后跳出循环
                    splitSmallRandomAccessFile.write(cache,0,(int)splitSmallFileActualSize);
                    break;
                }
            }
            splitRandomAccessFile.close();
            splitSmallRandomAccessFile.close();
        }
        return splitSmallFileList;
    }
    private void merge(File folder,File mergeFile)  throws IOException{
        //输出流，写文件,true表示追加写而不覆盖
        OutputStream outputStream=new BufferedOutputStream(new FileOutputStream(mergeFile,false));
        //输入流，读文件
        Vector<InputStream> vector=new Vector<>();
        File[] files = folder.listFiles();
        for (int i = 0; i < files.length; i++) {
            vector.add(new BufferedInputStream(new FileInputStream(files[i])));
        }
        //SequenceInputStream，实现批量输入流的按序列读
        SequenceInputStream sequenceInputStream=new SequenceInputStream(vector.elements());
        //10字节的缓存
        byte[] cache=new byte[1024*10];
        int len=-1;
        while ((len=sequenceInputStream.read(cache))!=-1) {
            //分段写
            outputStream.write(cache,0,len);
        }
        //强制将所有缓冲的输出字节被写入磁盘，更可靠
        outputStream.flush();
        outputStream.close();
        sequenceInputStream.close();
        for (File file : files) {
            file.delete();
        }
    }
    /**
     * 本地文件上传接口
     * 
     * @param file 上传的文件
     * @return 访问地址
     * @throws Exception
     */
    @Override
    public String uploadFile(MultipartFile file) throws Exception
    {
        String name = FileUploadUtils.upload(localFilePath, file);
        String url = domain + localFilePrefix + name;
        return url;
    }



    @Override
    public String uploadFileSequence(MultipartFile file, String folderPath) throws Exception {
        String path = sequencePath+File.separator +folderPath;
        File targetFolder = new File(path);
        targetFolder.mkdirs();
        path = path +File.separator+ file.getSize() +"-"+file.getOriginalFilename();
        File newFile = new File(path);
        String absPath = FileUploadUtils.getAbsoluteFile(targetFolder.getPath(), newFile.getName()).getAbsolutePath();
        file.transferTo(Paths.get(absPath));
        if(newFile.getName().endsWith("gz")){
            String decompressPath=newFile.getParentFile().getPath();
            String decompressName = FileUploadUtils.decompress(newFile,decompressPath);
            newFile = new File(decompressPath+File.separator+decompressName);
        }
        Charset newFileCharset = CharsetUtil.charset(FileUtils.getCharset(newFile));
        if(!CharsetUtil.CHARSET_UTF_8.equals(newFileCharset)){
            CharsetUtil.convert(newFile,newFileCharset,CharsetUtil.CHARSET_UTF_8);
        }
        return newFile.getName();
    }
}
